home *** CD-ROM | disk | FTP | other *** search
- NAME mssrcv
- ; File MSSRCV.ASM
- include mssdef.h
- ; Copyright (C) 1985, 1993, Trustees of Columbia University in the
- ; City of New York. Permission is granted to any individual or institution
- ; to use this software as long as it is not sold for profit. This copyright
- ; notice must be retained. This software may not be included in commercial
- ; products without written permission of Columbia University.
- ;
- ; Edit history
- ; 27 August 1992 version 3.13
- ; 6 Sept 1991 version 3.11
- ; Last edit 14 April 1993
- ; Sliding Windows
-
- public read2, read, rrinit, ackpak, nakpak, rstate
-
- setattr equ 57h ; DOS get/set file's date and time
-
- data segment
- extrn encbuf:byte, decbuf:byte, fmtdsp:byte, flags:byte, trans:byte
- extrn dtrans:byte, sstate:byte, diskio:byte, auxfile:byte
- extrn maxtry:byte, fsta:word, errlev:byte, kstatus:word
- extrn rpacket:byte, wrpmsg:byte, numpkt:word, windlow:byte
- extrn charids:word, windflag:byte, denyflg:word, chkparflg:byte
- extrn tfilsz:word
-
- cemsg db 'User intervention',0
- erms11 db 'Not enough disk space for file',0
- erms13 db 'Unable to send reply',0
- erms14 db 'No response from the host',0
- erms15 db 'Error. No buffers in receive routine',0
- erms29 db 'Rejecting file: ',0
- erms30 db 'File size',0
- erms31 db 'Date/time',0
- erms32 db 'Mailer request',0
- erms33 db 'File Type',0
- erms34 db 'Transfer Char-set',0
- erms36 db 'Unknown reason',0
- infms1 db cr,' Receiving: In progress',cr,lf,'$'
- infms3 db 'Completed',cr,lf,'$'
- infms4 db 'Failed',cr,lf,'$'
- infms6 db 'Interrupted',cr,lf,'$'
- infms7 db 'Discarding $'
- filhlp2 db ' Local path or filename or carriage return$'
- ender db bell,bell,'$'
- crlf db cr,lf,'$'
- badrcv db 0 ; local retry counter
- filopn db 0 ; non-zero if disk file is open
- ftime db 0,0 ; file time (defaults to 00:00:00)
- fdate db 0,0 ; file date (defaults to 1 Jan 1980)
- attrib db 0 ; attribute code causing file rejection
- rstate db 0 ; state of automata
- permchrset dw 0 ; permanent file character set holder
- temp dw 0
- data ends
-
- code1 segment
- extrn bufclr:far, pakptr:far, bufrel:far, makebuf:far, chkwind:far
- extrn firstfree:far, getbuf:far, pakdup:far
- extrn rpack:far, spack:far, fcsrtype:far
- code1 ends
-
- code segment
- extrn gofil:near, comnd:near, cntretry:near, perpr:near
- extrn serini:near, spar:near, rpar:near
- extrn init:near, cxmsg:near, cxerr:near, perpos:near
- extrn ptchr:near, ermsg:near, winpr:near, dskspace:near
- extrn stpos:near, rprpos:near, packlen:near
- extrn dodec:near, doenc:near, errpack:near, intmsg:near
- extrn ihostr:near, prtasz:near, begtim:near
- extrn endtim:near, pktsize:near,strlen:near,strcpy:near
- extrn msgmsg:near, clrbuf:near, pcwait:near, goopen:near
- extrn filekind:near, filecps:near
-
- assume cs:code, ds:data, es:nothing
-
- ; Data structures comments.
- ; Received packet material is placed in buffers pointed at by [si].bufadr;
- ; SI is typically used as a pointer to a pktinfo packet structure.
- ; Sent packet material (typically ACK/NAKs) is placed in a standard packet
- ; structure named rpacket.
- ; Rpack and Spack expect a pointer in SI to the pktinfo structure for the
- ; packet.
-
- ; RECEIVE command
-
- READ PROC NEAR
- mov bx,offset filhlp2 ; help message
- mov dx,offset auxfile ; local file name string
- mov byte ptr auxfile,0 ; clear it first
- mov ah,cmword ; local override filename/path
- call comnd
- jc read1a ; c = failure
- mov ah,cmeol ; get a confirm
- call comnd
- jc read1a ; c = failure
- mov rstate,'R' ; set state to receive initiate
- mov flags.xflg,0
- call serini ; initialize serial port
- jnc read1b ; nc = success
- or errlev,ksrecv ; set DOS error level
- or fsta.xstatus,ksrecv ; set status, failed
- or kstatus,ksrecv ; global status
- test flags.remflg,dquiet ; quiet display mode?
- jnz read1a ; nz = yes. Don't write to screen
- mov ah,prstr
- mov dx,offset infms4 ; Failed message
- int dos
- stc
- read1a: ret ; return failure
-
- read1b: call rrinit ; init variables for read
- call clrbuf ; clear serial port buffer
- call ihostr ; initialize the host
- cmp flags.destflg,2 ; destination is screen?
- je read2 ; e = yes
- call init ; setup display form
-
- ; Called by GET & SRVSND, display ok
- READ2: mov kstatus,kssuc ; global status, success
- mov windflag,0 ; init windows in use display flag
- mov numpkt,0 ; set the number of packets to zero
- mov badrcv,0 ; local retry counter
- mov fsta.pretry,0 ; clear total retry counter
- mov flags.cxzflg,0 ; reset ^X/^Z flag
- mov ax,flags.chrset ; permanent character set (Code Page)
- mov permchrset,ax ; remember here around attributes ptks
- cmp fmtdsp,0 ; formatted display?
- je read2a ; e = no
- call stpos
- mov ah,prstr ; Receiving in progress msg
- mov dx,offset infms1
- int dos
- read2a: jmp dispatch
- READ ENDP
-
- ; Call the appropriate action routines for each state of the protocol machine.
- ; State is held in byte rstate. Enter at label dispatch.
-
- dispatch proc near ; dispatch on state variable rstate
- mov ah,rstate ; get current state
- cmp ah,'R' ; Receive initiate state?
- jne dispat2 ; ne = no
- call rinit
- jmp short dispatch
-
- dispat2:cmp ah,'F' ; File header receive state?
- jne dispat3
- call rfile ; receive file header
- jmp short dispatch
-
- dispat3:cmp ah,'D' ; Data receive state?
- jne dispat4
- call rdata ; get data packets
- jmp short dispatch
-
- dispat4:cmp ah,'Z' ; EOF?
- jne dispat5
- call reof ; do EOF wrapup
- jmp short dispatch
-
- dispat5:cmp ah,'E' ; ^C or ^E abort?
- jne dispat6 ; ne = no
- mov bx,offset cemsg ; user intervention message
- call errpack ; send error message
- call intmsg ; show interrupt msg for Control-C-E
-
- ; Receive Complete state processor
- dispat6:cmp rstate,'C' ; completed normally?
- jne dispat6a ; ne = no
- cmp flags.cxzflg,0 ; interrupted?
- je dispat7 ; e = no, ended normally
- dispat6a:or errlev,ksrecv ; set DOS error level
- or fsta.xstatus,ksrecv+ksuser ; set status, failed + intervention
- or kstatus,ksrecv+ksuser ; global status
- dispat7:xor ax,ax ; tell statistics this is a receive operation
- call endtim ; stop file statistics accumulator
- call filecps ; show file chars/sec
- call bufclr ; release all buffers
- mov windlow,0
- mov ax,permchrset ; permanent character set (Code Page)
- mov flags.chrset,ax ; restore external version
- cmp rstate,'C' ; receive complete state?
- je dispat8 ; e = yes
- or errlev,ksrecv ; Failed, set DOS error level
- or fsta.xstatus,ksrecv ; set status, failed
- or kstatus,ksrecv ; global status
- call fileclose ; close output file
- call filedel ; delete incomplete file
-
- dispat8:cmp flags.destflg,2 ; receiving to screen?
- je dispa11 ; e = yes, nothing to clean up
- test flags.remflg,dquiet+dserial ; quiet or serial display mode?
- jnz dispa11 ; nz = yes, keep going
- mov al,1 ; underline cursor
- call fcsrtype ; set IBM-PC cursor to underline
- cmp flags.xflg,0 ; writing to the screen?
- jne dispa11 ; ne = yes
- call stpos ; position cursor to status line
- mov dx,offset infms3 ; completed message
- cmp rstate,'C' ; receive complete state?
- je dispa10 ; e = yes
- mov dx,offset infms4 ; failed message
- cmp flags.cxzflg,0 ; interrupted?
- je dispa10 ; e = no, ended normally
- mov dx,offset infms6 ; interrupted message
- dispa10:mov ah,prstr
- int dos
- cmp flags.belflg,0 ; bell desired?
- je dispa11 ; e = no
- mov ah,prstr
- mov dx,offset ender ; ring the bell
- int dos
- dispa11:call rprpos ; put cursor at reprompt position
- mov flags.cxzflg,0 ; clear flag for next command
- mov auxfile,0 ; clear receive-as filename buffer
- mov flags.xflg,0 ; clear to-screen flag
- mov diskio.string,0 ; clear active filename buffer
- mov fsta.xname,0 ; clear statistics external name
- clc ; return to ultimate caller, success
- ret
- dispatch endp
-
- ; Receive routines
-
- ; Receive initiate packet (tolerates E F M S X Y types)
- RINIT PROC NEAR
- mov windlow,0 ; lowest acceptable packet number
- mov trans.chklen,1 ; Use 1 char for init packet
- mov chkparflg,1 ; check for unexpected parity
- call rcvpak ; get a packet
- jnc rinit2 ; nc = success
- ret
-
- rinit2: mov ah,[si].pktype ; examine packet type
- cmp ah,'S' ; Send initiate packet?
- je rinit6 ; e = yes, process 'S' packet
- cmp ah,'M' ; Message packet?
- jne rinit4 ; ne = no
- call msgmsg ; display message
- mov trans.chklen,1 ; send Init checksum is always 1 char
- call ackpak0 ; ack and release packet
- ret
-
- rinit4: cmp ah,'F' ; File receive?
- je rinit5 ; e = yes
- cmp ah,'X' ; File receive to screen?
- je rinit5 ; e = yes
- cmp ah,'Y' ; ACK to a REMOTE command?
- jne rinit4a ; ne = no
- call msgmsg ; show any message in the ACK
- mov rstate,'C' ; Completed state
- ret
-
- rinit4a:call bufrel ; release this packet buffer
- ret ; and ignore it
-
- rinit5: mov rstate,'F' ; File header receive state
- ret
-
- ; 'S' packet received
- rinit6: call spar ; negotiate parameters
- push si
- mov si,offset rpacket ; build response in this packet
- call rpar ; report negotiated parameters
- pop si
- mov ah,trans.chklen ; negotiated checksum length
- push ax ; save it
- mov trans.chklen,1 ; use 1 char for init packet reply
- mov rstate,'F' ; set state to file header
- call ackpak ; ack with negotiated data
- pop ax ; recover working checksum
- mov trans.chklen,ah
- mov cx,trans.rlong ; negotiated length of received pkts
- call makebuf ; remake buffering for new windowing
- call packlen ; compute packet length
- ret
- RINIT ENDP
-
-
- ; Receive file header (tolerates E F M X Z types)
-
- RFILE PROC NEAR
- call rcvpak ; receive next packet
- jnc rfile1 ; nc = success
- ret
-
- rfile1: cmp [si].pktype,'Z' ; EOF?
- jne rfile2 ; ne = no, try next type
- mov rstate,'Z' ; change to EOF state, SI is valid pkt
- ret
-
- rfile2: cmp [si].pktype,'F' ; file header (F or X packet)?
- je rfil3a ; e = yes, 'F' pkt
- cmp [si].pktype,'X' ; visual display header?
- jne rfile5 ; ne = neither one
-
- rfile3: mov flags.xflg,1 ; 'X', say receiving to the screen
- rfil3a: mov filopn,0 ; assume not writing to a disk file
- call dodec ; decode packet
- call cxmsg ; clear Last Message line
- xor al,al ; say starting receive operation
- call begtim ; start statistics gathering
- mov al,dtrans.xchset ; reset Transmission char set
- mov trans.xchset,al ; to the current user default
- mov al,dtrans.xtype ; ditto for File Type
- mov trans.xtype,al
- mov ax,permchrset ; permanent character set (Code Page)
- mov flags.chrset,ax ; active character set
- call gofil ; open the output file
- jnc rfile4 ; nc = success
- jmp giveup ; failure, dx has message pointer
-
- rfile4: push si
- push di
- mov si,offset decbuf ; local filename is here
- mov di,offset encbuf ; destination is encoding buffer
- mov byte ptr [di],' ' ; leave space for protocol char
- inc di ; so other Kermits do not react
- call strcpy ; copy it, to echo local name to host
- dec di
- mov dx,di
- call strlen ; get length to cx for doenc
- mov si,offset rpacket ; use this packet buffer
- call doenc ; encode buffer, cx gets length
- pop di
- pop si
- mov rstate,'D' ; set the state to data receive
- call filekind ; report Text/Bin, char set
- jmp ackpak ; ack the packet, with filename
-
- rfile5: mov ah,[si].pktype ; get reponse packet type
- cmp ah,'B' ; 'B' End Of Transmission?
- jne rfile6 ; ne = no
- mov rstate,'C' ; set state to Complete
- jmp ackpak0 ; ack the packet
-
- rfile6: cmp ah,'M' ; Message packet?
- jne rfile7 ; ne = no
- call msgmsg ; display message
- jmp ackpak0 ; ack packet, stay in this state
-
- rfile7: call bufrel ; release buffer
- ret ; and ignore unknown packet
- RFILE ENDP
-
- ; Get file attributes from packet
- ; Recognize file size in bytes and kilobytes (used if bytes missing),
- ; file time and date. Reject Mail commands. Return carry clear for success,
- ; carry set for failure. If rejecting place reason code in byte attrib.
-
- GETATT PROC NEAR
- push es
- les bx,[si].datadr ; pointer to data field
- getat0: push bx
- sub bx,word ptr [si].datadr ; bx = length to examine
- cmp bx,[si].datlen ; are we beyond end of data?
- pop bx
- jl getat1 ; l = not yet
- pop es
- clc
- ret ; has carry clear for success
-
- getat1: cmp byte ptr es:[bx],'1' ; Byte length field?
- jne getat2 ; ne = no
- test flags.attflg,attlen ; allowed to examine file length?
- jnz getat1a ; nz = yes
- jmp getatunk ; z = no, ignore
- getat1a:mov al,es:[bx] ; remember attribute
- mov attrib,al
- inc bx ; pointer
- push si
- call getas ; get file size
- call spchk ; check available disk space
- pop si
- jnc getat0 ; nc = have enough space for file
- pop es
- ret ; return failure
-
- getat2: cmp byte ptr es:[bx],'!' ; Kilobyte length field?
- jne getat3 ; ne = no
- test flags.attflg,attlen ; allowed to examine file length?
- jnz getat2b ; nz = yes
- getat2a:jmp getatunk ; z = no, ignore
- getat2b:mov al,es:[bx] ; remember attribute
- mov attrib,al
- inc bx ; pointer
- call getak ; get file size
- jc getat2a ; carry means decode rejected
- push si
- call spchk ; check available disk space
- pop si
- jnc short getat0
- pop es
- ret ; return failure
-
- getat3: cmp byte ptr es:[bx],'#' ; date field?
- jne getat4 ; ne = no
- mov word ptr ftime,0 ; clear time and date fields
- mov word ptr fdate,0
- test flags.attflg,attdate ; allowed to update file date/time?
- jnz getat3a ; nz = yes
- jmp getatunk ; z = no, ignore
- getat3a:mov al,es:[bx] ; remember attribute
- mov attrib,al
- inc bx
- call getatd ; get file date
- jmp short getat0
-
- getat4: cmp byte ptr es:[bx],'+' ; Disposition?
- jne getat5 ; ne = no
- mov al,es:[bx] ; remember attribute
- mov attrib,al
- cmp byte ptr es:[bx+2],'M' ; Mail indicator?
- je getat4c ; e = yes, fail
- cmp byte ptr es:[bx+2],'P' ; REMOTE PRINT?
- jne getat4b ; ne = no, ignore field
- test flags.remflg,dserver ; acting as a server now?
- jz getat4a ; z = no
- test denyflg,prtflg ; is this server command disabled?
- jnz getat4c ; nz = yes, disabled
- getat4a:mov word ptr diskio.string,'RP' ; output to PRN
- mov word ptr diskio.string+2,'N' ; ignore options
- getat4b:jmp getatunk ; ignore field
- getat4c:stc ; set carry for failure
- pop es
- ret
-
- getat5: cmp byte ptr es:[bx],'"' ; File Type?
- jne getat6 ; ne = no
- test flags.attflg,atttype ; allowed to examine file type?
- jnz getat5a ; nz = yes
- jmp getatunk ; z = no, ignore
- getat5a:mov attrib,'"' ; remember attribute
- inc bx ; length field
- xor ch,ch
- mov cl,es:[bx] ; get length
- inc bx
- sub cl,20h ; remove ascii bias
- jc getat5d ; c = error in length, fail
- cmp byte ptr es:[bx],'A' ; Type letter (A, B, I), Ascii?
- jne getat5b ; ne = no
- mov trans.xtype,0 ; say Ascii/Text file type
- add bx,cx ; step to next field
- jmp getat0 ; next item please
- getat5b:cmp byte ptr es:[bx],'B' ; "B" Binary?
- jne getat5d ; ne = no, fail
- cmp cl,2 ; full "B8"?
- jb getat5c ; b = no, just "B"
- cmp byte ptr es:[bx+1],'8' ; proper length?
- jne getat5d ; ne = no
- getat5c:mov trans.xtype,1 ; say Binary
- add bx,cx ; step to next field
- jmp getat0 ; next item please
- getat5d:stc ; set carry for rejection
- pop es
- ret
-
- getat6: cmp byte ptr es:[bx],'*' ; character set usage?
- jne getat6d ; ne = no
- test flags.attflg,attchr ; allowed to examine char-set?
- jnz getat6a ; nz = yes
- getat6d:jmp getatunk ; z = no, ignore
- getat6a:mov attrib,'*' ; remember attribute
- inc bx ; length field
- xor ch,ch
- mov cl,es:[bx] ; get length
- inc bx
- sub cl,20h ; remove ascii bias
- js getat6c ; c = length error, fail
- mov trans.xchset,0 ; assume Transparent Transfer char-set
- cmp byte ptr es:[bx],'A' ; Normal Transparent?
- jne getat6b ; be = not Transparent
- add bx,cx ; point at next field
- clc
- jmp getat0
- getat6b:cmp byte ptr es:[bx],'C' ; character set?
- je getat7 ; e = yes
- getat6c:stc ; set carry for rejection
- pop es
- ret
- getat7: push di ; examine transfer character set
- push si
- mov di,bx ; point at first data character
- add bx,cx ; point bx beyond the text
- dec cx ; deduct leading 'C' char from count
- inc di ; skip the 'C'
- push bx ; save bx
- mov bx,offset charids ; point to array of char set info
- mov ax,[bx] ; number of members
- mov temp,ax ; loop counter
- mov trans.xchset,xfr_xparent ; assume xfer char set Transparent
- getat7a:add bx,2 ; point to a member's address
- mov si,[bx] ; point at member [length, string]
- cmp cl,[si] ; string lengths the same?
- jne getat7b ; ne = no, try the next member
- inc si ; point at ident string
- cld
- push cx ; save incoming count
- push di ; save incoming string pointer
- repe cmpsb ; compare cx characters
- pop di
- pop cx
- je getat7d ; e = idents match
- getat7b:inc trans.xchset ; try next set
- dec temp ; one less member to consider
- jnz getat7a ; nz = more members to try
- pop bx ; failure to find a match
- pop si
- pop di
- mov trans.xchset,xfr_xparent; use Transparent for unknown char set
- cmp flags.unkchs,0 ; keep the file?
- je getat7c ; e = yes, regardless of unk char set
- pop es
- stc ; set carry for rejection
- ret
- getat7c:jmp getat0 ; report success anyway
-
- getat7d:pop bx ; a match, use current trans.xchset
- pop si
- pop di
- cmp trans.xchset,xfr_cyrillic ; using Transfer Char Set Cyrillic?
- jne getat7e ; ne = no
- mov flags.chrset,866 ; force CP866 (required by Cyrillic)
- clc
- jmp getat0
- getat7e:cmp trans.xchset,xfr_japanese ; using Trans Char Set Japanese-EUC?
- jne getat7f ; ne = no
- mov flags.chrset,932 ; force Shift-JIS
- clc
- jmp getat0 ; success
- getat7f:cmp trans.xchset,xfr_latin2 ; using Trans Char Set Latin-2?
- jne getat7g ; ne = no
- mov flags.chrset,852 ; force CP852
- clc
- jmp getat0 ; success
- getat7g:cmp trans.xchset,xfr_hebiso ; using Hebrew-ISO?
- jne getat7h ; ne = no
- mov flags.chrset,862 ; force CP862
- getat7h:clc ; success
- jmp getat0
-
- ; workers for above
- getatunk:inc bx ; Unknown. Look at length field
- mov al,es:[bx]
- sub al,' ' ; remove ascii bias
- xor ah,ah
- inc ax ; include length field byte
- add bx,ax ; skip to next attribute
- jmp getat0
- ; Decode File length (Byte) field
- getas: mov cl,es:[bx] ; length of file size field
- inc bx ; point at file size data
- sub cl,' ' ; remove ascii bias
- xor ch,ch
- xor ax,ax ; current length, bytes
- xor dx,dx
- jcxz getas3 ; z = empty field
- getas2: push cx
- shl dx,1 ; high word of size, times two
- mov di,dx ; save
- shl dx,1
- shl dx,1 ; times 8
- add dx,di ; yields dx * 10
- mov di,dx ; save dx
- xor dx,dx
- mov cx,10 ; also clears ch
- mul cx ; scale up previous result in ax
- mov cl,es:[bx] ; get a digit
- inc bx
- sub cl,'0' ; remove ascii bias
- add ax,cx ; add to current length
- adc dx,0 ; extend result to dx
- add dx,di ; plus old high part
- pop cx
- loop getas2
- mov diskio.sizelo,ax ; low order word
- mov diskio.sizehi,dx ; high order word
- clc
- ret
- getas3: dec bx ; backup
- stc ; fail the decode
- ret
- ; Decode Kilobyte attribute
- getak: mov ax,diskio.sizelo ; current filesize, low word
- add ax,diskio.sizehi
- or ax,ax ; zero if not used yet
- jz getak1 ; z = not used before
- dec bx ; backup pointer
- stc ; set carry to ignore this field
- ret
-
- getak1: call getas ; parse as if Byte field
- jnc getak2 ; nc = parsed ok
- ret ; c = failure
- getak2: mov ax,diskio.sizelo ; get low word of size
- mov dx,diskio.sizehi ; high word
- mov dh,dl ; times 256
- mov dl,ah
- mov ah,al
- xor al,al
- shl dx,1 ; times four to make times 1024
- shl dx,1
- rol ax,1 ; two high bits of ah to al
- rol ax,1
- and al,3 ; keep them
- or dl,al ; insert into high word
- xor al,al
- mov diskio.sizehi,dx ; store high word
- mov diskio.sizelo,ax ; store low word
- clc ; clear carry
- ret
- ; File date and time
- getatd: mov word ptr ftime,1 ; two seconds past midnight
- mov word ptr fdate,0
- mov dl,es:[bx] ; field length
- xor dh,dh
- sub dl,' ' ; remove ascii bias
- inc bx ; next field
- add dx,bx ; where next field begins
- mov temp,dx ; save in temp
- cmp byte ptr es:[bx+6],' ' ; short form date (yymmdd)?
- je getad2 ; e = yes
- add bx,2 ; skip century digits (19)
- getad2: mov ax,10
- mov dx,es:[bx] ; get year tens and units digits
- add bx,2 ; dl has tens, dh has units
- sub dx,'00' ; remove ascii bias
- mul dl ; ax = high digit times ten
- add al,dh ; units digit
- sub ax,80 ; remove rest of 1980 bias
- jns getad2a ; ns = no sign = non-negative result
- xor ax,ax ; don't store less than 1980
- getad2a:shl al,1 ; adjust for DOS bit format
- mov fdate+1,al
- mov ax,es:[bx] ; get month digits
- add bx,2
- sub ax,'00' ; remove ascii bias
- or al,al ; tens digit set?
- jz getad2b ; z = no
- add ah,10 ; add to units digit
- getad2b:cmp ah,8 ; high bit of month set?
- jb getad3 ; b = no
- or fdate+1,1
- sub ah,8 ; and deduct it here
- getad3: mov cl,5
- shl ah,cl ; normalize months bits
- mov fdate,ah
- mov dx,es:[bx] ; do day of the month
- add bx,2 ; dh has units, dl has tens digit
- sub dx,'00' ; remove ascii bias
- mov ax,10
- mul dl ; ax = ten times tens digit
- add al,dh ; plus units digit
- or fdate,al
- cmp bx,temp ; are we at the end of this field?
- jae getad5 ; ae = yes, prematurely
- inc bx ; skip space separator
- mov ax,10 ; prepare for hours
- mov dx,es:[bx] ; hh digits
- add bx,2
- sub dx,'00' ; remove ascii bias
- mul dl ; 10*high digit of hours
- add al,dh ; plus low digit of hours
- mov cl,3 ; normalize bits
- shl al,cl
- mov ftime+1,al ; store hours
- inc bx ; skip colon
- mov ax,10 ; prepare for minutes
- mov dx,es:[bx] ; mm digits
- add bx,2
- sub dx,'00' ; remove ascii bias
- mul dl ; 10*high digit of minutes
- add al,dh ; plus low digit of minutes
- xor ah,ah
- mov cl,5 ; normalize bits
- shl ax,cl
- or ftime+1,ah ; high part of minutes
- mov ftime,al ; low part of minutes
- cmp bx,temp ; are we at the end of this field
- jae getad5 ; ae = yes, quit here
- inc bx ; skip colon
- mov ax,10 ; prepare for seconds
- mov dx,es:[bx] ; ss digits
- add bx,2
- sub dx,'00' ; remove ascii bias
- mul dl ; 10*high digit of seconds
- add al,dh ; plus low digit of seconds
- shr al,1 ; store as double-seconds for DOS
- or ftime,al ; store seconds
- getad5: ret
- GETATT ENDP
-
- ; Receive data (tolerates A D E M Z types)
-
- RDATA PROC NEAR
- call rcvpak ; get next packet
- jnc rdata1 ; nc = success
- ret ; else return to do new state
-
- rdata1: mov ah,[si].pktype ; check packet type
- cmp ah,'D' ; Data packet?
- je rdata3 ; e = yes
- cmp ah,'A' ; Attributes packet?
- je rdata4 ; e = yes
- cmp ah,'M' ; Message packet?
- jne rdat2 ; ne = no
- call msgmsg ; display message
- jmp ackpak0 ; ack the packet, stay in this state
-
- rdat2: cmp ah,'Z' ; EOF packet?
- jne rdat2a ; ne = no
- mov rstate,'Z' ; next state is EOF, do not ack yet
- ret
-
- rdat2a: call bufrel ; Unknown packet type, release buffer
- ret ; and ignore it
- ; D data packets
- rdata3: cmp filopn,2 ; file opened yet?
- je rdata3b ; e = yes
- call goopen ; open it now
- jnc rdata3a ; nc = success
- jmp giveup ; failure, dx has message pointer
- rdata3a:mov filopn,2 ; say file is open now
- rdata3b:call ptchr ; decode 'D' packet, output to file
- jc rdat3c ; c = failure to write output
- jmp ackpak0 ; ack the packet, stay in this state
-
- rdat3c: mov dx,offset erms11 ; cannot store all the data
- jmp giveup ; tell the other side
-
- ; 'A' packet, analyze
- rdata4: call getatt ; get file attributes from packet
- mov cx,0 ; reply length, assume 0/nothing
- jnc rdat4b ; nc = success, attributes accepted
- mov cx,2 ; 2 bytes, declining the file
- mov encbuf,'N' ; decline the transfer
- mov al,attrib ; get attribute causing rejection
- mov encbuf+1,al ; report rejection reason to sender
- or fsta.xstatus,ksrecv ; set status, failed
- mov kstatus,ksrecv ; global status, failed
- mov flags.cxzflg,'X' ; set this in case host ignores 'N'
- test flags.remflg,dquiet ; quiet display?
- jnz rdat4b ; nz = yes
- push si
- push cx
- push ax
- mov dx,offset erms29 ; say rejecting the file
- call ermsg ; show rejecting file, then reason
- pop ax
- mov dx,offset erms30
- cmp al,'1' ; Byte count?
- je rdat4a ; e = yes
- cmp al,'!' ; Kilobyte count?
- je rdat4a ; e = yes
- mov dx,offset erms31
- cmp al,'#' ; Date and Time?
- je rdat4a ; e = yes
- mov dx,offset erms32
- cmp al,'+' ; Mail?
- je rdat4a ; e = yes
- mov dx,offset erms33
- cmp al,'"' ; File Type?
- je rdat4a
- mov dx,offset erms34
- cmp al,'*' ; Transfer Char-set?
- je rdat4a
- mov dx,offset erms36 ; unknown reason
- rdat4a: call prtasz ; display reason
- pop cx
- pop si
- rdat4b: push si
- mov si,offset rpacket ; encode to this packet
- call doenc ; do encoding
- pop si
- call filekind ; report Text/Bin, char set
- jmp ackpak ; ACK the attributes packet
- rdata endp
-
- ; End of File processor (expects Z type to have been received elsewhere)
- ; Enter with packet pointer in SI to a 'Z' packet.
- reof proc near ; 'Z' End of File packet
- cmp flags.cxzflg,0 ; interrupted?
- jne reof3 ; ne = yes, no 100% done indicator
- cmp fmtdsp,0 ; formatted screen?
- je reof5 ; e = no, no message
- cmp wrpmsg,0 ; written Percentage done yet?
- je reof5 ; e = no
- mov ax,tfilsz ; obtained file size
- mov word ptr diskio.sizelo,ax ; force to original size
- mov ax,tfilsz+2
- mov word ptr diskio.sizehi,ax
- call perpr ; show percentage done, 100%
- jmp short reof5 ; file close common code
-
- reof3: call intmsg ; show interrupt msg on local screen
- or errlev,ksrecv ; set DOS error level
- or fsta.xstatus,ksrecv+ksuser ; set status, failed + intervention
- mov kstatus,ksrecv+ksuser ; global status
- cmp flags.cxzflg,'X' ; kill one file?
- jne reof5 ; ne = no
- mov flags.cxzflg,0 ; clear ^X so next file survives
- ; common code for file closing
- reof5: cmp filopn,2 ; file opened yet?
- je reof5b ; e = yes
- call goopen ; open it now
- jnc reof5a ; nc = success
- jmp giveup ; failure, dx has message pointer
- reof5a: mov filopn,2 ; say file is open now
- reof5b: call fileclose ; close the file
- call dodec ; decode incoming packet to decbuf
- cmp decbuf,'D' ; is the data "D" for discard?
- jne reof7 ; ne = no, write out file
- call filedel ; delete file incomplete file
- or errlev,ksrecv ; set DOS error level
- or fsta.xstatus,ksrecv+ksuser ; set status, failed + intervention
- mov kstatus,ksrecv+ksuser ; global status
-
- reof7: mov rstate,'F'
- call ackpak0 ; acknowledge the packet
- mov diskio.string,0 ; clear file name
- ret
- reof endp
-
- ; init variables for read
- rrinit proc near
- mov trans.windo,1 ; one window slot before negotiations
- mov cx,drpsiz ; default receive pkt length (94)
- call makebuf ; construct & clear all buffer slots
- call packlen ; compute packet length
- xor ax,ax
- mov numpkt,ax ; set the number of packets to zero
- mov windlow,al ; starting sequence number of zero
- mov fsta.pretry,ax ; set the number of retries to zero
- mov filopn,al ; say no file opened yet
- mov windflag,al ; windows in use init flag
- mov fmtdsp,al ; no formatted display yet
- mov diskio.string,al ; clear active filename buffer
- mov fsta.xname,al ; clear statistics external name
- ret
- rrinit endp
-
- ; Deliver packets organized by sequence number.
- ; Delivers a packet pointer in SI whose sequence number matches windlow.
- ; If necessary a new packet is requested from the packet recognizer. Failures
- ; to receive are managed here and may generate NAKs. Updates formatted screen.
- ; Store packets which do not match windlow, process duplicates and strays.
- ; Error packet and ^C/^E interrupts are detected and managed here.
- ; Return success with carry clear and SI holding the packet structure address.
- ; Return failure with carry set, maybe with a new rstate.
-
- rcvpak proc near
- mov al,windlow ; sequence number we want
- call pakptr ; find pkt pointer with this seqnum
- mov si,bx ; the packet pointer
- jnc rcvpa1a ; nc = got one, else read fresh pkt
- call getbuf ; get a new buffer address into si
- jnc rcvpa1 ; nc = success
- mov bx,offset erms15 ; insufficient buffers
- jmp giveup
-
- rcvpa1: call winpr ; show window slots in use
- call rpack ; receive a packet, si has buffer ptr
- jc rcvpa2 ; c = failure to receive, analyze
- inc numpkt ; increment the number of packets
- cmp flags.xflg,0 ; receiving to screen?
- jne rcvpa1a ; ne = yes, skip displaying
- cmp flags.destflg,2 ; destination is screen?
- je rcvpa1a ; e = yes
- call pktsize ; report packet qty and size
- rcvpa1a:jmp rcvpa6 ; success, validate
- ; ------------------- failure to receive any packet -------------------------
- ; Reception failed. What to do?
- rcvpa2: call cntretry ; update retries, detect ^C, ^E
- jc rcvpa2a ; c = exit now from ^C, ^E
- call bufrel ; discard unused buffer
- inc badrcv ; count receive retries
- mov al,badrcv ; count # bad receptions in a row
- cmp al,maxtry ; too many?
- jb rcvpa4 ; b = not yet, NAK intelligently
- mov dx,offset erms14 ; no response from host
- jmp giveup ; tell the other side
-
- rcvpa2a:call bufrel ; discard unwanted buffer
- stc ; set carry for failure
- ret ; move to Error state
-
- ; do NAKing
- rcvpa4: mov al,windlow ; Timeout or Crunched packet
- add al,trans.windo ; find next slot after last good
- dec al
- and al,3fh ; start at window high
- mov ah,-1 ; set a not-found marker
- mov cl,trans.windo ; cx = number of slots to examine
- xor ch,ch
- rcvpa4a:call pakptr ; sequence number (in AL) in use?
- jnc rcvpa4b ; nc = yes, stop here
- mov ah,al ; remember seqnum of highest vacancy
- dec al ; work backward in sequence numbers
- and al,3fh
- loop rcvpa4a
-
- rcvpa4b:mov al,ah ; last-found empty slot (-1 = none)
- cmp ah,-1 ; found a vacant slot?
- jne rcvpa4c ; ne = no, else use first free seqnum
- call firstfree ; set AL to first open slot
- jc rcvpa4d ; c = no free slots, an error
- rcvpa4c:mov rpacket.seqnum,al ; NAK this unused sequence number
- call nakpak ; NAK using rpacket
- jc rcvpa4d ; c = failure on sending operation
- stc ; rcv failure, stay in current state
- ret
-
- rcvpa4d:mov dx,offset erms13 ; failure, cannot send reply
- jmp giveup ; show msg, change states
- ; ------------------------- received a packet ------------------------------
- ; remove duplicates, validate sequence number
- rcvpa6: mov badrcv,0 ; clear retry counter
- cmp [si].pktype,'E' ; Error packet? Accept w/any seqnum
- jne rcvpa6a ; ne = no
- jmp error ; display message, change states
-
- rcvpa6a:mov al,[si].seqnum ; this packet's sequence number
- mov rpacket.seqnum,al ; save here for reply
- call pakdup ; set ah to number of copies
- cmp ah,1 ; more than one copy?
- jbe rcvpa7 ; be = no, just one
- call bufrel ; discard duplicate
- mov al,rpacket.seqnum ; recover current sequence number
- call pakptr ; get packet pointer for original
- mov si,bx ; should not fail if pakdup works ok
- jnc rcvpa7 ; nc = ok, work on the original again
- ret ; say failure, stay in current state
-
- rcvpa7: call chkwind ; validate sequence number (cx=status)
- jc rcvpa7b ; c = outside current window
- mov al,[si].seqnum ; get sequence number again
- cmp al,windlow ; is it the desired sequence number?
- jne rcvpa7a ; ne = no, do not change states yet
- clc
- ret ; return success, SI has packet ptr
-
- rcvpa7a:stc ; not desired pkt, stay in this state
- ret ; do not increment retry counter here
-
- rcvpa7b:or cx,cx ; inside previous window?
- jg rcvpa7c ; g = outside any window, ignore it
- mov al,[si].pktype ; get packet Type
- cmp al,'I' ; let 'I' and 'S' pkts be reported
- je rcvpa7d ; even if in previous window, to
- cmp al,'S' ; accomodate lost ack w/data
- je rcvpa7d
- cmp al,'Y' ; maybe our ACK echoed?
- je rcvpa7c ; e = yes, discard
- cmp al,'N' ; or our NAK echoed?
- je rcvpa7c ; e = yes, discard
- call ackpak0 ; previous window, ack and ignore it
- stc ; rcv failure, stay in current state
- ret
-
- rcvpa7c:call bufrel ; ignore packet outside of any window
- stc ; rcv failure, stay in current state
- ret
-
- rcvpa7d:mov rstate,'R' ; redo initialization when 'I'/'S'
- stc ; are observed, keep current pkt
- ret
- rcvpak endp
-
- ; Send ACK packet. Enter with rpacket data field set up.
- ; ACKPAK sends ack with data, ACKPAK0 sends ack without data.
- ackpak proc near ; send an ACK packet
- cmp rpacket.datlen,0 ; really just no data?
- jne ackpa2 ; ne = no, send prepared ACK packet
- ackpak0:mov rpacket.datlen,0 ; no data
- cmp flags.cxzflg,0 ; user interruption?
- je ackpa2 ; e = no
- push cx ; yes, send the interrupt character
- push si
- mov si,offset rpacket
- mov cl,flags.cxzflg ; send this so host knows about ^X/^Z
- mov encbuf,cl ; put datum into the encode buffer
- mov cx,1 ; data size of 1 byte
- call doenc ; encode, char count is in cx
- pop si
- pop cx
- ackpa2: mov rpacket.pktype,'Y' ; ack packet
- mov rpacket.numtry,0
- ackpa3: push si
- mov si,offset rpacket
- call spack ; send the packet
- pop si
- jnc ackpa4 ; nc = success
- cmp flags.cxzflg,'C' ; Control-C abort?
- je ackpa3a ; e = yes, quit now
- cmp flags.cxzflg,'E' ; Control-E abort?
- je ackpa3a ; e = yes, quit now
- push ax ; send failure, retry
- mov ax,100 ; 0.1 sec
- call pcwait ; small wait between retries
- inc rpacket.numtry
- mov al,rpacket.numtry
- cmp al,maxtry ; exceeded retry limit?
- pop ax
- jbe ackpa3 ; be = ok to try again
- mov sstate,'A' ; set states to abort
- mov rstate,'A'
- mov rpacket.numtry,0
- mov dx,offset erms13 ; unable to send reply
- jmp giveup
- ackpa3a:stc ; set carry for failure
- ret
-
- ackpa4: mov al,rpacket.seqnum ; success
- mov rpacket.datlen,0 ; clear old contents
- call pakptr ; acking an active buffer?
- jc ackpa5 ; c = no such seqnum, stray ack
- push si
- mov si,bx ; packet pointer from pakptr
- call bufrel ; release ack'ed packet
- pop si
- mov rpacket.numtry,0
- cmp al,windlow ; acking window low?
- jne ackpa5 ; ne = no
- mov al,windlow ; yes, rotate the window
- inc al
- and al,3fh
- mov windlow,al
- ackpa5: clc
- ret
- ackpak endp
-
- ; Send a NAK. Uses rpacket structure.
- NAKPAK proc near
- mov rpacket.numtry,0
- nakpa2: push si
- mov si,offset rpacket
- mov [si].datlen,0 ; no data
- inc fsta.nakscnt ; count NAKs sent
- mov [si].pktype,'N' ; NAK that packet
- call spack
- pop si
- jc nakpa3 ; c = failure
- mov rpacket.numtry,0
- clc
- ret ; return success
-
- nakpa3: cmp flags.cxzflg,'C' ; Control-C abort?
- je nakpa3a ; e = yes, quit now
- cmp flags.cxzflg,'E' ; Control-E abort?
- je nakpa3a ; e = yes, quit now
- push ax ; send failure, retry
- mov ax,100 ; wait 0.1 second
- call pcwait
- inc rpacket.numtry ; count attempts to respond
- mov al,rpacket.numtry
- cmp al,maxtry ; tried enough times?
- pop ax
- jbe nakpa2 ; be = ok to try again
- mov sstate,'A' ; set states to abort
- mov rstate,'A'
- mov rpacket.numtry,0
- mov dx,offset erms13 ; unable to send reply
- jmp giveup
- nakpa3a:stc
- ret ; return failure
- NAKPAK ENDP
-
- ; Close, but do not delete, output file. Update file attributes,
- ; add Control-Z or Control-L, if needed.
- fileclose proc near
- cmp filopn,0 ; is a file open?
- jne filec0 ; ne = yes
- ret
- filec0: cmp flags.xflg,0 ; receiving to screen?
- jne filec2 ; ne = yes
- cmp flags.destflg,1 ; destination is disk?
- jne filec1 ; ne = no
- cmp flags.eofcz,0 ; should we write a ^Z?
- je filec1 ; e = no, keep going
- cmp trans.xtype,0 ; test mode tranfer?
- jne filec2 ; ne = no, binary, no ^Z
- push si
- mov rpacket.datlen,1 ; one byte to decode and write
- push es
- les si,rpacket.datadr ; source buffer address
- mov byte ptr es:[si],'Z'-40h ; put Control-Z in buffer
- pop es
- mov si,offset rpacket ; address for decoder
- call ptchr ; decode and write to output
- pop si
- filec1: cmp flags.destflg,0 ; file destination is printer?
- jne filec2 ; ne = no, skip next part
- push si
- mov rpacket.datlen,1 ; one byte to decode and write
- push es
- les si,rpacket.datadr ; source buffer address
- mov byte ptr es:[si],'L'-40h ; put Control-L (FF) in buffer
- pop es
- mov si,offset rpacket ; address for decoder
- call ptchr ; decode and write to output
- pop si
- filec2: mov ah,write2 ; write to file
- xor cx,cx ; write 0 bytes to truncate length
- mov bx,diskio.handle ; file handle
- or bx,bx ; valid handle?
- jl filec5 ; l = no
- int dos
- xor al,al ; get device info
- mov ah,ioctl
- int dos
- test dl,80h ; bit set if handle is for a device
- jnz filec4 ; nz = non-disk, no file attributes
- ; do file attributes and close
- mov cx,word ptr ftime ; new time
- mov dx,word ptr fdate ; new date
- mov word ptr fdate,0
- mov word ptr ftime,0 ; clear current time/date attributes
- mov ax,cx
- or ax,dx
- jz filec4 ; z = no attributes to set
- or cx,cx ; time set as null?
- jnz filec3 ; nz = no
- inc cl ; two seconds past midnight
- filec3: mov ah,setattr ; set file date/time attributes
- mov al,1 ; set, not get
- mov bx,diskio.handle ; file handle
- int dos ; end of file attributes
- filec4: mov bx,diskio.handle ; file handle
- push dx ; save dx
- mov ah,close2 ; close file
- int dos
- pop dx
- mov filopn,0 ; say file is closed
- filec5: ret
- fileclose endp
-
- ; Delete file whose asciiz name is in diskio.string
- filedel proc near
- mov dx,offset diskio.string ; file name, asciiz
- xor ax,ax
- cmp diskio.string,al ; filename present?
- je filede2 ; e = no
- cmp flags.abfflg,al ; keep incomplete file?
- je filede2 ; e = yes
- test flags.remflg,dquiet ; quiet display?
- jnz filede1 ; nz = yes
- cmp flags.xflg,al ; receiving to screen?
- jne filede1 ; ne = yes, no message
- push dx
- call cxmsg ; clear Last message line
- mov dx,offset infms7 ; saying Discarding file
- mov ah,prstr
- int dos
- pop dx
- call prtasz ; show filename
- filede1:mov ah,del2 ; delete the file
- int dos
- filede2:ret
- filedel endp
-
- ; Error exit. Enter with dx pointing to asciiz error message.
- ; Sends 'E' Error packet and shows message on screen. Changes state to 'A'.
- ; Always returns with carry set.
- giveup proc near
- cmp flags.destflg,2 ; receiving to the screen?
- je giveu1 ; e = yes, no formatted display
- call ermsg ; show msg on error line
- giveu1: mov bx,dx ; set bx to error message
- call errpack ; send error packet just in case
- mov rstate,'A' ; change the state to abort
- stc ; set carry
- ret
- giveup endp
-
- ; ERROR sets abort state, positions the cursor and displays the Error message.
-
- ERROR PROC NEAR
- mov rstate,'A' ; set state to abort
- call dodec ; decode to decbuf
- mov dx,offset decbuf ; where msg got decoded, asciiz
- call ermsg ; show string
- stc ; set carry for failure state
- ret
- ERROR ENDP
-
- ; Called by GETATT in receiver code to verify sufficient disk space.
- ; Gets file path from diskio.string setup in mssfil, remote size in diskio
- ; from getatt, and whether a disk file or not via ioctl on the file handle.
- ; Returns carry clear if enough space.
- spchk proc near ; check for enough disk space
- push ax
- push bx
- push cx
- push dx
- mov ah,ioctl ; ask DOS about this file handle
- xor al,al ; get info
- mov bx,diskio.handle
- int dos
- test dl,80h ; handle is a disk file?
- jnz spchk5b ; nz = no, always enough space
- mov ah,gcurdsk ; get current disk
- int dos
- add al,'A' ; make 0 == A
- mov cl,al ; assume this drive
- mov dx,word ptr diskio.string ; filename used in open
- cmp dh,':' ; drive letter given?
- jne spchk1 ; ne = no
- mov cl,dl ; get the letter
- and cl,not 20h ; convert to upper case
- spchk1: call dskspace ; calculate space into dx:ax
- jc spchk6 ; c = error
- push ax ; save low word of bytes
- push dx ; save high word, dx:ax
- mov dx,diskio.sizehi ; high word of file size dx:ax
- mov ax,diskio.sizelo ; low word
- mov cx,dx ; copy size long word to cx:bx
- mov bx,ax
- shr bx,1 ; divide long word by two
- shr cx,1
- jnc spchk2 ; nc = no carry down
- or bx,8000h ; get carry down
- spchk2: shr bx,1 ; divide by two again
- shr cx,1
- jnc spchk3
- or bx,8000h ; get carry down
- spchk3: shr bx,1 ; divide long word by two
- shr cx,1
- jnc spchk4 ; nc = no carry down
- or bx,8000h ; get carry down
- spchk4: shr bx,1 ; divide long word by two
- shr cx,1
- jnc spchk5 ; nc = no carry down
- or bx,8000h ; get carry down
- spchk5: add ax,bx ; form dx:ax = (17/16) * dx:ax
- adc dx,cx
- pop cx ; high word of disk space
- pop bx ; low word
- sub bx,ax ; minus inflated file size, low word
- sbb cx,dx ; and high word
- js spchk6 ; s = not enough space for file
- spchk5b:clc
- jmp short spchk7 ; enough space
- spchk6: stc
- spchk7: pop dx
- pop cx
- pop bx
- pop ax
- ret
- spchk endp
-
- code ends
- end
-